<?php
/**
 * Created by PhpStorm.
 * User: longli
 * Date: 2020/08/15
 * Time: 20:15
 * @link http://www.lmterp.cn
 */

namespace app\common\service\product;


use app\common\library\Tools;
use app\common\model\Category;
use app\common\model\Orders;
use app\common\model\Producer;
use app\common\model\Product;
use app\common\model\ProductAttr;
use app\common\model\ProductBrand;
use app\common\model\ProductImage;
use app\common\model\ProductPlatformSku;
use app\common\model\ProductPurchase;
use app\common\model\ProductSpec;
use app\common\model\ProductStore;
use app\common\model\ProductUnit;
use app\common\service\BaseService;
use Env;
use Exception;
use think\Db;
use think\exception\DbException;
use think\facade\Log;
use think\Paginator;

class ProductService extends BaseService
{
    /**
     * 搜索商品
     * @param array $search
     * @return Paginator
     * @date 2020/09/02
     * @author longli
     */
    public function search($search = [])
    {
        // 处理搜索条件
        $sField = ['sku', 'attr', 'color']; $purField = ['producer_id', 'producer_name']; $pField = Product::getTableFields();
        $pWhere = $sWhere = $purWhere = [];
        if(!empty($search['start_date'])) $pWhere[] = array_merge($pWhere, $this->parseLayuiRangeDate('create_time', $search['start_date']));

        if(isset($search['create_date'])) $pWhere = array_merge($pWhere, $this->parseScopeDateToWhere('create_time', $search['create_date']));

        foreach($search as $k => $v)
        {
            if(in_array($k, $pField))
            {
                if($k == 'category_id') $v = array_column(Category::getAllSon($v)->toArray(), 'cate_id');
                $pWhere[] = [$k, 'in', $v];
            }
            else if(in_array($k, $sField))
            {
                $sWhere[$k] = $v;
            }
            else if(in_array($k, $purField))
            {
                $purWhere[$k] = $v;
            }
        }
        // 处理排序
        $sort = 'product_id desc';
        if(!empty($search['sort']) && $search['sort'] != 'product_id') $sort = "{$search['sort']} desc";

        // 模型关联
        $productModel = Product::where($pWhere)->order($sort)->with([
            'stores',
            'purchase' => function($query){$query->field(["product_id", "producer_name", "url", "is_default"]);},
            "admin"   => function($query){$query->field(["id","nickname"]);},
            'sbrand' => function($query){$query->field("brand_id,name_ch");},
            'category' => function($query){$query->field("cate_id,name");},
            'unit' => function($query){$query->field("unit_id,name_ch");},
            'images' => function($query){$query->field("product_id,path");},
        ]);
        // 处理平台 sku
        if(!empty($search['platform_sku']))
        {
            $productModel->where("product_id", "in", function($query)use($search)
            {
                $query->table(ProductPlatformSku::getTable())->where("platform_sku", "like", "%{$search['platform_sku']}%")->field("product_id");
            });
        }
        // 处理变体查询
        if(!empty($sWhere))
        {
            $productModel->where("product_id", "in", function($query) use($sWhere)
            {
                $query->table(ProductStore::getTable())->field("product_id");
                foreach($sWhere as $k => $v)
                {
                    $k == 'sku' && !Tools::contains($v, ',')
                      ? $query->where("sku", "like", "%{$v}%")
                      : $query->where($k, 'in', $v);
                }
            });
        }
        // 处理商品采购查询
        if(!empty($purWhere))
        {
            $productModel->where("product_id", "in", function($query) use($purWhere)
            {
                $query->table(ProductPurchase::getTable())->field("product_id");
                foreach($purWhere as $k => $v)
                {
                    $query->where($k, $v);
                }
            });
        }
        // 处理分页
        $limit = !empty($search['limit']) ? $search['limit'] : null;
        return $productModel->paginate($limit);
    }

    /**
     * 添加产品
     * @param array $productData 产品数据
     * @return Product|bool
     * @date 2020/09/05
     * @author longli
     */
    public function addProduct($productData = [])
    {
        $productData = $this->parseRequestData($productData);
        try
        {
            Db::startTrans();
            $pData = Product::getFilterField($productData['product'], '');
            if(empty($pData)) return false;
            $pData = Tools::trim($pData);
            if(empty($pData['image_url']) && !empty($productData['image']['images'])) $pData['image_url'] = current($productData['image']['images']);
            if(isset($pData['product_id'])) $product = Product::get($pData['product_id']);
            if(!empty($product))
            {
                $product->save($pData);
            }
            else
            {
                $product = Product::create($pData);
                $product = Product::get($product->product_id);
            }
            if(!empty($productData['store']))
            {
                $this->addStore($product, $productData['store']);
                if(!empty($productData['stock']['stock']))
                {
                    $productData['stock']['stock'] = Tools::replaceArrayKey($productData['store']['sku'], $productData['stock']['stock']);
                    $this->addStock($product, $productData['stock']);
                }
            }
            if(!empty($productData['image'])) $this->addImages($product, $productData['image']['images']);
            if(!empty($productData['producer']) && empty($product['purchase'])) $this->addProductPurchase($product, $productData['producer']);
            if(!empty($productData['purchase']) && empty($product['producer'])) $this->addProductPurchase($product, $productData['purchase']);
            if(!empty($productData['attr'])) $this->addAttr($product, $productData['attr']['values']);
            Db::commit();
            return $product;
        }catch(DbException $exception)
        {
            Db::rollback();
            Log::info(sprintf("产品录入失败，错误信息【%s】", $exception->getMessage()));
            return false;
        }
    }

    /**
     * 添加产品详情
     * @param Product $product 产品
     * @param array $storeData 产品详情
     * @date 2020/09/05
     * @author longli
     */
    public function addStore(Product $product, $storeData = [])
    {
        $keys = array_keys($storeData);
        $ndids = [];
        foreach($storeData[$keys[0]] as $key => $item)
        {
            $temp = [];
            foreach($keys as $k)
            {
                $temp[$k] = isset($storeData[$k][$key]) ? $storeData[$k][$key] : '';
            }
            $data = ProductStore::getFilterField($temp, '');
            if(empty($data)) continue;
            $data = Tools::trim($data);
            $store = null;
            if(isset($data['store_id'])) $store = ProductStore::get($data['store_id']);
            if(empty($store) && !empty($data['sku'])) $store = ProductStore::get(['sku' => $data['sku']]);
            if(empty($data['image_url'])) $data['image_url'] = !empty($product->image_url) ? $product->image_url : '';
            $data['product_id'] = $product->product_id;
            if(!empty($store))
            {
                $store->save($data);
            }
            else
            {
                $store = ProductStore::create($data);
            }
            $ndids[] = $store->store_id;
        }
        // 删除变体
        ProductStore::destroy(function($query) use($product, $ndids)
        {
            $query->where('product_id', 'eq', $product->product_id);
            if(!empty($ndids)) $query->where('store_id', 'not in', $ndids);
        });
    }

    /**
     * 录入商品初始化库存信息
     * @param Product $product 商品信息
     * @param array $stockData 库存信息
     * @date 2020/09/16
     * @author longli
     */
    protected function addStock(Product $product, $stockData = [])
    {
        if(!isset($stockData['warehouse_id']) || !isset($stockData['stock']))return;
        foreach($stockData['warehouse_id'] as $w)
        {
            foreach($product->stores as $store)
            {
                if(!isset($stockData['stock'][$store->sku]) || ($s = intval($stockData['stock'][$store->sku])) < 1) continue;
                $data = [
                    'store_id'     => $store->store_id,
                    'warehouse_id' => $w,
                    'sku'          => $store->sku,
                    'stock'        => $s,
                ];
                WarehouseService::getInstance()->addStock($store->store_id, $data);
            }
        }
    }

    /**
     * 添加商品采购信息
     * @param Product $product 产品
     * @param array $purchase 采购信息
     * @date 2020/09/05
     * @author longli
     */
    public function addProductPurchase(Product $product, $purchase = [])
    {
        $keys = array_keys($purchase);
        $ndppId = [];
        foreach($purchase[$keys[0]] as $key => $value)
        {
            $temp = [];
            foreach($keys as $k)
            {
                $temp[$k] = isset($purchase[$k][$key]) ? $purchase[$k][$key] : '';
            }
            $data = ProductPurchase::getFilterField($temp, '');
            if(empty($data)) continue;
            $data = Tools::trim($data);
            $data['product_id'] = $product->product_id;
            $data['is_default'] = !empty($data['is_default']) ? ProductPurchase::IS_YES : ProductPurchase::IS_NO;
            $data['status'] = !empty($data['status']) ? ProductPurchase::IS_YES : ProductPurchase::IS_NO;
            if(!empty($data['producer_id']) && $producer = Producer::get($data['producer_id'])) $data['producer_name'] = $producer->name;
            if(isset($data['pp_id']) && $purchaseObj = ProductPurchase::get($data['pp_id']))
            {
                $purchaseObj->save($data);
            }
            else
            {
                $purchaseObj = ProductPurchase::create($data);
                $purchaseObj = ProductPurchase::get($purchaseObj->pp_id);
            }
            $ndppId[] = $purchaseObj->pp_id;
            if(!empty($data['is_default']))
            {
                $product->default_purchase = $purchaseObj->producer_name;
                $product->price = $purchaseObj->price;
                $product->save();
            }
        }
        // 删除商品采购信息
        ProductPurchase::destroy(function($query) use($product, $ndppId)
        {
            $query->where('product_id', 'eq', $product->product_id);
            if(!empty($ndppId)) $query->where('pp_id', 'not in', $ndppId);
        });
    }

    /**
     * 添加图片
     * @param Product $product 产品
     * @param string|string[] $images 图片
     * @date 2020/09/05
     * @author longli
     */
    public function addImages(Product $product, $images = [])
    {
        if(!is_array($images)) $images = explode(',', $images);
        $images = Tools::trim($images);
        try
        {
            foreach (ProductImage::where(['product_id' => $product->product_id])->select() as $item)
            {
                if(!in_array($item->path, $images)) $item->delete();
            }
        }catch(Exception $e){}
        foreach($images as $image)
        {
            if(ProductImage::where(['path' => $image, 'product_id' => $product->product_id])->count()) continue;
            $data = [
                'path' => $image,
                'product_id' => $product->product_id,
            ];
            if($info = Tools::getImageInfo(Env::get("root_path") . "public{$image}")) $data += $info;
            ProductImage::create($data);
        }
    }

    /**
     * 添加属性
     * @param Product $product 产品详情
     * @param int|int[] $attrs 属性
     * @date 2020/09/05
     * @author longli
     */
    public function addAttr($product, $attrs = [])
    {
        if(!is_array($attrs)) $attrs = explode(',', $attrs);
        try
        {
            foreach (ProductAttr::where(['product_id' => $product->product_id])->select() as $item)
            {
                if(!in_array($item->spec_id, $attrs)) $item->delete();
            }
        }catch(Exception $e){}
        foreach($attrs as $attr)
        {
            $attrObj = ProductAttr::get(['product_id' => $product->product_id, 'spec_id' => $attr]);
            $data = [
                'spec_id' => $attr,
                'product_id' => $product->product_id
            ];
            if($spce = ProductSpec::get($attr))
            {
                $data += [
                    'name' => $spce->name,
                    'value' => $spce->value,
                ];
            }
            !empty($attrObj) ? $attrObj->save($data) : ProductAttr::create($data);
        }
    }

    /**
     * 解析表单请求数据
     * @param array $data 请求数据
     * @param bool $prefix 是否保留前缀，默认不保留
     * @return array
     * @date 2020/09/09
     * @author longli
     */
    public function parseRequestData($data = [], $prefix = false)
    {
        $ret = ['other' => []];
        foreach($data as $k => $v)
        {
            $temp = explode('.', $k);
            if(count($temp) == 2)
            {
                $pre = $temp[0];
                if(!isset($ret[$pre])) $ret[$pre] = [];
                if($pre == 'image' && !is_array($v)) $v = explode(',', $v);
                $tk = $prefix ? $k : $temp[1];
                $ret[$pre] += [$tk => $v];
            }
            else
            {
                $ret['other'] += [$k => $v];
            }
        }
        return $ret;
    }

    /**
     * 删除产品
     * @param int|int[] $ids 产品id
     * @return bool
     * @date 2020/09/05
     * @author longli
     */
    public function deleteProductById($ids)
    {
        try
        {
            Db::startTrans();
            Product::destroy($ids);
            ProductImage::destroy(function($query)use($ids){$query->where("product_id", "in", $ids);});
            ProductStore::destroy(function($query)use($ids){$query->where("product_id", "in", $ids);});
            ProductAttr::destroy(function($query)use($ids){$query->where("product_id", "in", $ids);});
            ProductPurchase::destroy(function($query)use($ids){$query->where("product_id", "in", $ids);});
            Db::commit();
            return true;
        }catch(DbException $exception)
        {
            Db::rollback();
            Log::info(sprintf("商品删除失败，提示信息【%s】", $exception->getMessage()));
        }
        return false;
    }

    /**
     * 获取 sku 总重量，单位(g)
     * @param array|Orders $skus
     * @return float
     * @date 2020/09/05
     * @example $skus = [
     *      'A001' => 2,
     *      'A002' => 1,
     * ];
     * @author longli
     */
    public function getTotalWeight($skus = [])
    {
        if(empty($skus)) return 0;
        if($skus instanceof Orders)
        {
            $temp = $skus->detail->toArray();
            $skus = array_combine(array_column($temp, 'sku'), array_column($temp, 'qty'));
        }
        $total = 0;
        try
        {
            foreach(ProductStore::field(["sku", "weight"])->where("sku", "in", array_keys($skus))->select() as $store)
            {
                if($store->weight > 0) $total += $store->weight * $skus[$store->sku];
            }
        }catch(Exception $e){}
        return $total;
    }

    /**
     * 生成内部同类sku
     * @param int $num 生成多少个，默认1个, 同类sku最大700个
     * @param int $len sku 位数默认，8位
     * @return string|string[]
     * @date 2020/09/09
     * @author longli
     */
    public function generateSameSku($num = 1, $len = 8)
    {
        if($num < 1 || $num > 700) return [];
        $sku = $this->generateSku(1, $len);
        if($num == 1) return $sku;
        $ret = [];
        $prefix = $suffix = "";
        $base = 65;
        $p = 0;
        for($i = 0; $i < $num; $i++)
        {
            if($i > 25 && $i % 26 == 0) $prefix = chr($base + $p++);
            $t = $i < 26 ? $i : $i % 26;
            $suffix = $prefix . chr($base + $t);
            $ret[] = $sku . "-{$suffix}";
        }
        return $ret;
    }

    /**
     * 添加相同 sku
     * @param string $sku sku
     * @param int $num 要添加的数量，默认为1个, 同类sku最大700个
     * @return string|string[]
     * @date 2020/09/10
     * @author longli
     */
    public function addSameSku($sku, $num = 1)
    {
        if($num < 1 || $num > 700) return [];
        $tsku = explode('-', $sku)[0];
        $psku = ProductStore::where("sku", "like", "$tsku%")->order(Db::raw("LENGTH(sku) desc,sku desc"))->value("sku");
        if(empty($psku)) return "";
        $prefix = $suffix = '';
        $temp = explode('-', $psku);
        $sku = $temp[0];
        if(count($temp) > 1)
        {
           if(strlen($temp[1]) > 1)
           {
               $prefix = $temp[1][0];
               $suffix = $temp[1][1];
           }
           else
           {
               $suffix = $temp[1];
           }
        }
        $ret = [];
        for($i = 0; $i < $num; $i++)
        {
            $suffix = ord($suffix) + 1;
            if($suffix > 90)
            {
                $suffix = 65;
                $p = !empty($prefix) ? ord($prefix) + 1 : 65;
                $prefix = chr($p);
            }
            $suffix = chr($suffix);
            $ret[] = $sku . "-{$prefix}{$suffix}";
        }
        if($num == 1) return $ret[0];
        return $ret;
    }

    /**
     * 生成内部 sku
     * @param int $num 生成多少个，默认1个
     * @param int $len sku 位数默认，8位
     * @return string|string[]
     * @date 2020/09/09
     * @author longli
     */
    public function generateSku($num = 1, $len = 8)
    {
        if($num < 1) return [];
        $ret = [];
        for($i = 0; $i < $num; $i++)
        {
            do
            {
                $sku = strtoupper(Tools::getRandStr($len, 2));
            }while(in_array($sku, $ret) || ProductStore::where(['sku' => $sku])->count());
            $ret[] = $sku;
        }
        if($num == 1) return $ret[0];
        return $ret;
    }

    /**
     * 添加商品分类
     * @param array $data 分类信息
     * @return Category|bool
     * @date 2020/09/18
     * @author longli
     */
    public function addCategory($data = [])
    {
        $cDate = Category::getFilterField($data);
        if(empty($cDate)) return false;
        $cDate = Tools::trim($cDate);
        $cate = null;
        if(isset($cDate['cate_id'])) $cate = Category::get($cDate['cate_id']);
        if(!empty($cate))
        {
            $cate->save($cDate);
        }
        else
        {
            $cate = Category::create($cDate);
        }
        return $cate;
    }

    /**
     * 添加规格
     * @param array $data 规格信息
     * @return ProductSpec|bool
     * @date 2020/09/18
     * @author longli
     */
    public function addSpec($data = [])
    {
        $sData = ProductSpec::getFilterField($data);
        if(empty($sData)) return false;
        $sData = Tools::trim($sData);
        $spec = null;
        if(isset($sData['spec_id'])) $spec = ProductSpec::get($sData['spec_id']);
        if(!empty($spec))
        {
            $spec->save($sData);
        }
        else
        {
            $spec = ProductSpec::create($sData);
        }
        return $spec;
    }

    /**
     * 添加计量单位
     * @param array $data 计量单位信息
     * @return ProductUnit|bool
     * @date 2020/09/18
     * @author longli
     */
    public function addUnit($data = [])
    {
        $uData = ProductUnit::getFilterField($data);
        if(empty($uData)) return false;
        $uData = Tools::trim($uData);
        $unit = null;
        if(isset($uData['unit_id'])) $unit = ProductUnit::get($uData['unit_id']);
        if(!empty($unit))
        {
            $unit->save($uData);
        }
        else
        {
            $unit = ProductUnit::create($uData);
        }
        return $unit;
    }

    /**
     * 添加品牌
     * @param array $data 品牌信息
     * @return ProductBrand|bool
     * @date 2020/09/18
     * @author longli
     */
    public function addBrand($data = [])
    {
        $bData = ProductBrand::getFilterField($data);
        if(empty($bData)) return false;
        $bData = Tools::trim($bData);
        $brand = null;
        if(isset($bData['brand_id'])) $brand = ProductBrand::get($bData['brand_id']);
        if(!empty($brand))
        {
            $brand->save($bData);
        }
        else
        {
            $brand = ProductBrand::create($bData);
        }
        return $brand;
    }
}